/* 
 * Copyright (c) 2012 JSquirrelEngine
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in 
 * the Software without restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
 * Software, and to permit persons to whom the Software is furnished to do so, subject
 * to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
 * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 * 
 */

package org.jsquirrel.math;

/**
 * TODO explain the matrix multiplication written on the ideas book
 * 
 * @author Juan David Adarve Bermudez
 *
 */
public class AffineTransform2D extends Transform2D {

	
	//*********************************************************************************************
	// INSTANCE ATTRIBUTES
	//*********************************************************************************************
	
	/**
	 * Displacement values
	 */
	private float dX, dY;
	
	/**
	 * Rotation axis
	 */
	private float rX, rY;
	
	/**
	 * Rotation angle
	 */
	private float angle;
	
	/**
	 * Scale factors
	 */
	private float sX, sY;
	
	
	//*********************************************************************************************
	// CONSTRUCTORS
	//*********************************************************************************************
	
	/**
	 * Creates a default transform with zero displacement, zero rotation, rotation axis placed at (0, 0)
	 * and scale factors equal to 1
	 */
	public AffineTransform2D() {
		
		angle = 0;
		dX = 0;
		dY = 0;
		rX = 0;
		rY = 0;
		sX = 1;
		sY = 1;
	}
	
	
	//*********************************************************************************************
	// PUBLIC METHODS
	//*********************************************************************************************
	
	@Override
	public void transform(final F2Vector vector) {
		
		float x = vector.x;
		float y = vector.y;
		
		float cos = (float)Math.cos(angle);
		float sin = (float)Math.sin(angle);
		
		float xMinusRx = x - rX;
		float yMinusRy = y - rY;
		
		vector.x = sX*(xMinusRx*cos + yMinusRy*sin + rX) + dX;
		vector.y = sY*(-xMinusRx*sin + yMinusRy*cos + rY) + dY; 
		
		//vector.x = x*sX*cos - y*sY*sin - rX*sX*cos + rY*sY*sin + dX;
		//vector.y = x*sX*sin + y*sY*cos - rX*sX*sin - rY*sY*cos + dY;
		
	}

	@Override
	public void transform(final F2Vector source, F2Vector target) {
		
		float x = source.x;
		float y = source.y;
		
		float cos = (float)Math.cos(angle);
		float sin = (float)Math.sin(angle);
		
		//target.x = x*sX*cos - y*sY*sin - rX*sX*cos + rY*sY*sin + dX;
		//target.y = x*sX*sin + y*sY*cos - rX*sX*sin - rY*sY*cos + dY;
		
		float xMinusRx = x - rX;
		float yMinusRy = y - rY;
		
		target.x = sX*(xMinusRx*cos + yMinusRy*sin + rX) + dX;
		target.y = sY*(-xMinusRx*sin + yMinusRy*cos + rY) + dY;
		
	}
	
	/**
	 * Sets the rotation angle.
	 * 
	 * @param theta: new rotation angle in radians
	 */
	public final void setAngle(float theta) {
		
		angle = theta;
	}
	
	/**
	 * Sets the displacement on the X axis
	 * 
	 * @param dX: new displacement value on X
	 */
	public final void setDx(float dX) {
		this.dX = dX;
	}
	
	/**
	 * Sets the displacement on the Y axis
	 * 
	 * @param dY: new displacement value on Y
	 */
	public final void setDy(float dY) {
		this.dY = dY;
	}
	
	/**
	 * Sets the rotation axis coordinate on the X axis
	 * 
	 * @param rX: new X rotation axis coordinate
	 */
	public final void setRotationAxisX(float rX) {
		this.rX = rX;
	}
	
	/**
	 * Sets the rotation axis coordinate on the Y axis
	 * 
	 * @param rY: new Y rotation axis coordinate
	 */
	public final void setRotationAxisY(float rY) {
		this.rY = rY;
	}
	
	/**
	 * Set the rotation axis
	 * 
	 * @param rX X coordinate
	 * @param rY Y coordinate
	 */
	public final void setRotationAxis(float rX, float rY) {
		this.rX = rX;
		this.rY = rY;
	}
	
	/**
	 * 
	 * @return returns the X coordinate of the rotation axis
	 */
	public final float getRotationAxisX() {
		return rX;
	}
	
	/**
	 * 
	 * @return returns the Y coordinate of the rotation axis
	 */
	public final float getRotationAxisY() {
		return rY;
	}
	
	/**
	 * Set the scale factor on the X axis
	 * 
	 * @param sX: new scale factor
	 */
	public final void setScaleX(float sX) {
		this.sX = sX;
	}
	
	/**
	 * Set the scale factor on the Y axis
	 * 
	 * @param sY: new scale factor
	 */
	public final void setScaleY(float sY) {
		this.sY = sY;
	}

}
